#include "scenenode.h"
#include "utils.h"

reseatable_reference <SceneNode> SceneNode::addNode(const SceneNode & newNode, const SceneNode * beforeNodeIfPossible)
{
	if (!canHaveChild(newNode))
		return reseatable_reference <SceneNode>();
	else
	{
		std::list <SceneNode>::iterator insertLocation = children.end();
		if (beforeNodeIfPossible)
		{
			for (std::list <SceneNode>::iterator i = children.begin(); i != children.end(); i++)
			{
				if (&*i == beforeNodeIfPossible)
				{
					std::cout << "addNode: beforeNodeIfPossible succeeded" << std::endl;
					insertLocation = i;
					break;
				}
			}
		}
		
		if (beforeNodeIfPossible && insertLocation == children.end())
			std::cout << "addNode: beforeNodeIfPossible failed" << std::endl;
		
		std::list <SceneNode>::iterator newChild = children.insert(insertLocation, newNode);
		return reseatable_reference <SceneNode>(*newChild);
	}
}

bool SceneNode::canHaveChild(const std::string & childType, const std::string & childCategory) const
{
	//std::cout << name << "::canHaveChild(" << childType << ", " << childCategory << ")" << std::endl;
	
	if (allowedChildren.size() == 1 && *allowedChildren.begin() == "*")
		return true;
	else
	{
		if (allowedChildren.find("category:"+childCategory) != allowedChildren.end())
			return true;
		
		return (allowedChildren.find(childType) != allowedChildren.end());
	}
}

bool SceneNode::deleteNode(const SceneNode & node)
{
	for (std::list <SceneNode>::iterator i = children.begin(); i != children.end(); i++)
	{
		if (&*i == &node)
		{
			children.erase(i);
			return true;
		}
	}
	
	for (std::list <SceneNode>::iterator i = children.begin(); i != children.end(); i++)
	{
		if (i->deleteNode(node))
			return true;
	}
	
	return false;
}

SceneNode * SceneNode::getParent(const SceneNode & node)
{
	const SceneNode * thisconst = this;
	const SceneNode * parent = thisconst->getParent(node);
	return const_cast<SceneNode*>(parent);
}

const SceneNode * SceneNode::getParent(const SceneNode & node) const
{
	for (std::list <SceneNode>::const_iterator i = children.begin(); i != children.end(); i++)
	{
		if (&node == &*i)
			return this;
		else
		{
			const SceneNode * parent = i->getParent(node);
			if (parent)
				return parent;
		}
	}
	
	return NULL;
}

const SceneNode * SceneNode::getNextSibling(const SceneNode & node) const
{
	for (std::list <SceneNode>::const_iterator i = children.begin(); i != children.end(); i++)
	{
		std::list <SceneNode>::const_iterator nexti = i;
		nexti++;
		if (&node == &*i && nexti != children.end())
			return &*nexti;
		else
		{
			const SceneNode * sibling = i->getNextSibling(node);
			if (sibling)
				return sibling;
		}
	}
	
	return NULL;
}

void SceneNode::getChildren(std::vector <const SceneNode*> & output, 
						 bool recurse,
						 const std::string & typeFilter,
						 const SceneNode * typeMask,
						 const std::string & noRecurseIntoType,
						 std::vector <std::string> * paths,
						 std::vector <std::string> curPath) const
{
	for (std::list <SceneNode>::const_iterator i = children.begin(); i != children.end(); i++)
	{
		if (paths)
			curPath.push_back(i->getName());
		
		if (typeFilter.empty() || i->getType() == typeFilter || "category:"+i->category == typeFilter)
		{
			output.push_back(&*i);
			
			if (paths)
			{
				paths->push_back(implode(curPath, "->"));
			}
		}
		
		if (recurse && 
			(!typeMask || i->getType() != typeMask->getType() || &*i == typeMask) && 
			(noRecurseIntoType != i->getType()))
		{
			i->getChildren(output, recurse, typeFilter, typeMask, noRecurseIntoType, paths, curPath);
		}
		
		if (paths)
			curPath.pop_back();
	}
}

std::tr1::shared_ptr <std::vector <float> > SceneNode::getFloatVars()
{
	updateFloatVars();
	
	return floatVars.pointer;
}

void SceneNode::updateFloatVars()
{
	if (!floatVars.pointer)
		floatVars.pointer = std::tr1::shared_ptr <std::vector <float> >(new std::vector <float>());
	
	floatVars.pointer->clear();
	for (int i = 0; i < 16; i++)
	{
		std::stringstream varname;
		varname << "v";
		varname.width(2);
		varname.fill('0');
		varname << i;
		std::string varstr = varname.str();
		const char * varstrc = varstr.c_str();
		fields_type::const_iterator v = fields.find(varname.str());
		if (v != fields.end())
		{
			float value(0);
			std::stringstream s(v->second.getValue());
			s >> value;
			floatVars.pointer->push_back(value);
		}
		else
		{
			break;
		}
	}
}

void SceneNode::setFloatVars(const double * array16)
{
	assert(array16);
	for (int i = 0; i < 16; i++)
	{
		std::stringstream varname;
		varname << "v";
		varname.width(2);
		varname.fill('0');
		varname << i;
		fields_type::iterator v = fields.find(varname.str());
		if (v != fields.end())
		{
			std::stringstream s;
			s << array16[i];
			v->second.setValue(s.str());
		}
		else
		{
			break;
		}
	}
	updateFloatVars();
}

void SceneNode::upgrade(const std::map <std::string, SceneNode> & nodeTypes)
{
	// check our type against the allowed types
	typedef std::map <std::string, SceneNode> nodeTypes_type;
	nodeTypes_type::const_iterator t = nodeTypes.find(type);
	if (t != nodeTypes.end())
	{
		fields_type oldFields = fields;
		fields = t->second.fields;
		
		// go through old fields and set any matching new fields to the old value
		for (fields_type::iterator f = oldFields.begin(); f != oldFields.end(); f++)
		{
			fields_type::iterator i = fields.find(f->first);
			if (i != fields.end())
			{
				i->second.upgradeFrom(f->second);
			}
		}
	}
	else
	{
		t = nodeTypes.find("information");
		if (t != nodeTypes.end())
		{
			std::string oldname = name;
			std::string oldtype = type;
			*this = t->second;
			name = oldname;
			fields["text"].setValue("The type \""+oldtype+"\" no longer exists.");
		}
	}
	
	// recurse
	for (std::list <SceneNode>::iterator i = children.begin(); i != children.end(); i++)
	{
		i->upgrade(nodeTypes);
	}
}

void SceneNode::convertPaths(const std::string & absoluteReferenceFile, bool absoluteToRelative)
{
	std::string absoluteReference = absoluteReferenceFile;
	systemToFileDirSep(absoluteReference);

	for (fields_type::iterator f = fields.begin(); f != fields.end(); f++)
	{
		if (f->second.getType() == "file")
		{
			std::string path = f->second.getValue();
			if (!path.empty())
			{
				if (absoluteToRelative)
					systemToFileDirSep(path);
				std::string converted = absoluteToRelative ? makeRelativePath(absoluteReference, path) : makeAbsolutePath(absoluteReference, path);
				f->second.setValue(converted);
			}
		}
	}
	
	// recurse
	for (std::list <SceneNode>::iterator i = children.begin(); i != children.end(); i++)
	{
		i->convertPaths(absoluteReference, absoluteToRelative);
	}
}

void SceneNode::handleNodeRename(const SceneNode & node, const std::string & oldname)
{
	for (std::list <SceneNode>::iterator i = children.begin(); i != children.end(); i++)
	{
		if (&*i != &node)
		{
			// examine all fields to look for node reference fields
			for (fields_type::iterator f = i->fields.begin(); f != i->fields.end(); f++)
			{
				SceneNodeField & field = f->second;
				if (field.getType().substr(0,5) == "node ")
				{
					std::string reftype = field.getType().substr(5);
					if (reftype == node.type || reftype == "category:"+node.category)
					{
						// this is a node selector field that references the same type of node as the renamed node
						// see if it's currently set to the renamed node's name
						// if so, set it to the new node name
						if (field.getValue() == oldname)
							field.setValue(node.getName());
					}
				}
			}
		}
	
		// recurse
		i->handleNodeRename(node, oldname);
	}
}
